/*
 * byte_stream.cpp
 *
 *  Created on: 2008-12-5
 *      Author: fanliangliang
 */

#include "byte_stream.h"
#include <cstring>
#include <fstream>

BEGIN_NAMESPACE_TOXIC

using namespace std;

static uint16_t swap16(uint16_t x) {
	return (x >> 8) | (x << 8);
}

static uint32_t swap32(uint32_t x) {
	return (x >> 24) | ((x & 0xff0000) >> 8) | ((x & 0xff00) << 8) | (x << 24);
}

static uint64_t swap64(uint64_t x) {
	return ((uint64_t)(swap32((uint32_t)x)) << 32) | swap32((uint32_t)(x >> 32));
}

static float swapf(float f) {
	union {
		float f;
		byte b[4];
	} x, y;

	x.f = f;
	y.b[0] = x.b[3];
	y.b[1] = x.b[2];
	y.b[2] = x.b[1];
	y.b[3] = x.b[0];
	return y.f;
}

static double swapd(double d) {
	union {
		double d;
		byte b[8];
	} x, y;

	x.d = d;
	y.b[0] = x.b[7];
	y.b[1] = x.b[6];
	y.b[2] = x.b[5];
	y.b[3] = x.b[4];
	y.b[4] = x.b[3];
	y.b[5] = x.b[2];
	y.b[6] = x.b[1];
	y.b[7] = x.b[0];
	return y.d;
}

static uint16_t noswap16(uint16_t x) {
	return x;
}

static uint32_t noswap32(uint32_t x) {
	return x;
}

static uint64_t noswap64(uint64_t x) {
	return x;
}

static float noswapf(float f) {
	return f;
}

static double noswapd(double d) {
	return d;
}




Network::Helper::Helper() {
	union {
		short a;
		char b[sizeof(short)];
	} u = { 0x0201 };
	byteOrder = (u.b[0] == 1) ? LITTLE_ENDIAN_ : BIG_ENDIAN_;

	if (byteOrder == LITTLE_ENDIAN_) {
		htol16 = noswap16;
		htol32 = noswap32;
		htol64 = noswap64;
		htolf = noswapf;
		htold = noswapd;

		htob16 = swap16;
		htob32 = swap32;
		htob64 = swap64;
		htobf = swapf;
		htobd = swapd;

		ltoh16 = noswap16;
		ltoh32 = noswap32;
		ltoh64 = noswap64;
		ltohf = noswapf;
		ltohd = noswapd;

		btoh16 = swap16;
		btoh32 = swap32;
		btoh64 = swap64;
		btohf = swapf;
		btohd = swapd;
	}
	else {
		htol16 = swap16;
		htol32 = swap32;
		htol64 = swap64;
		htolf = swapf;
		htold = swapd;

		htob16 = noswap16;
		htob32 = noswap32;
		htob64 = noswap64;
		htobf = noswapf;
		htobd = noswapd;

		ltoh16 = swap16;
		ltoh32 = swap32;
		ltoh64 = swap64;
		ltohf = swapf;
		ltohd = swapd;

		btoh16 = noswap16;
		btoh32 = noswap32;
		btoh64 = noswap64;
		btohf = noswapf;
		btohd = noswapd;
	}
}

Network::Helper::~Helper() {

}

Network::Helper& Network::helper() {
	static Helper _helper;
	return _helper;
}




ByteStream::ByteStream(ByteOrder byteOrder /*= BIG_ENDIAN_*/)
:_gcount(0)
,_gpos(0)
,_ppos(0)
,_byteOrder(byteOrder) {

}

ByteStream::ByteStream(const void* buffer, size_t length, ByteOrder byteOrder /*= BIG_ENDIAN_*/)
:_gcount(0)
,_gpos(0)
,_ppos(0)
,_byteOrder(byteOrder) {
	write(buffer, length);
}

ByteStream::~ByteStream() {

}

ByteStream& ByteStream::operator>>(uint8_t& i) {
	read_aux(&i, 1);
	return *this;
}

ByteStream& ByteStream::operator>>(int8_t& i) {
	return *this >> reinterpret_cast<uint8_t&>(i);
}

ByteStream& ByteStream::operator>>(int16_t& i) {
	return *this >> reinterpret_cast<uint16_t&>(i);
}

ByteStream& ByteStream::operator>>(int32_t& i) {
	return *this >> reinterpret_cast<uint32_t&>(i);
}

ByteStream& ByteStream::operator>>(int64_t& i) {
	return *this >> reinterpret_cast<uint64_t&>(i);
}

ByteStream& ByteStream::operator>>(bool& b) {
	uint8_t i = 0;
	*this >> i;
	b = (i == 0) ? false : true;
	return *this;
}

ByteStream& ByteStream::operator>>(char*& s) {
	if (size() - tellg() >= 4) {
		uint32_t length = 0;
		*this >> length;
		s = new char[length];
		if (!read_aux(s, length)) {
			s[_gcount] = '\0';
		}
	}
	return *this;
}

ByteStream& ByteStream::operator>>(string& s) {
	char* str = 0;
	*this >> str;
	if (str != 0) {
		s = str;
		delete [] str;
	}
	return *this;
}

ByteStream& ByteStream::operator<<(uint8_t i) {
	write(&i, 1);
	return *this;
}

ByteStream& ByteStream::operator<<(int8_t i) {
	return *this << uint8_t(i);
}

ByteStream& ByteStream::operator<<(int16_t i) {
	return *this << uint16_t(i);
}

ByteStream& ByteStream::operator<<(int32_t i) {
	return *this << uint32_t(i);
}

ByteStream& ByteStream::operator<<(int64_t i) {
	return *this << uint64_t(i);
}

ByteStream& ByteStream::operator<<(bool b) {
	return *this << uint8_t(b);
}

ByteStream& ByteStream::operator<<(const char* s) {
	uint32_t length = strlen(s) + 1;
	*this << length;
	return write(s, length);
}

ByteStream& ByteStream::operator<<(const string& s) {
	uint32_t length = s.size() + 1;
	*this << length;
	return write(s.c_str(), length);
}

ByteStream& ByteStream::read(void* buffer, size_t length) {
	size_t begin = _gpos;
	seekg(length);
	_gcount = _gpos - begin;
	if (_gcount > 0) {
		memcpy(buffer, &_data[begin], _gcount);
	}
	return *this;
}

ByteStream& ByteStream::write(const void* buffer, size_t length) {
	const byte* begin = static_cast<const byte*>(buffer);
	if (_ppos == size()) {
		_data.insert(_data.end(), begin, begin + length);
	}
	else {
		size_t n = size() - _ppos;
		if (n >= length) {
			copy(begin, begin + length, _data.begin() + _ppos);
		}
		else {
			copy(begin, begin + n, _data.begin() + _ppos);
			copy(begin + n, begin + length, back_inserter(_data));
		}
	}
	return seekp(length);
}

ByteStream& ByteStream::seekg(int pos, ios_base::seekdir dir /*= ios_base::cur*/) {
	seek_aux(_gpos, pos, dir);
	return *this;
}

ByteStream& ByteStream::seekp(int pos, ios_base::seekdir dir /*= ios_base::cur*/) {
	seek_aux(_ppos, pos, dir);
	return *this;
}

const byte* ByteStream::data() const {
	return _data.empty() ? 0 : &_data[0];
}

ByteStream::operator void*() const {
	return eof() ? 0 : reinterpret_cast<void*>(1);
}

bool ByteStream::operator !() const {
	return eof();
}

bool ByteStream::eof() const {
	return _gpos == size();
}

size_t ByteStream::gcount() const {
	return _gcount;
}

size_t ByteStream::size() const {
	return _data.size();
}

size_t ByteStream::tellg() const {
	return _gpos;
}

size_t ByteStream::tellp() const {
	return _ppos;
}

void ByteStream::dump(const char* filename) const {
	ofstream file(filename, ios::binary);
	file.write(reinterpret_cast<const char*>(data()), size());
	file.close();
}

bool ByteStream::read_aux(void* buffer, size_t length) {
	read(buffer, length);
	return _gcount == length;
}

void ByteStream::seek_aux(size_t& seekpos, int pos, ios_base::seekdir dir) {
	if (dir == ios_base::beg) {
		seek_cur(seekpos, 0, pos);
	}
	else if (dir == ios_base::cur) {
		seek_cur(seekpos, seekpos, pos);
	}
	else if (dir == ios_base::end) {
		seek_cur(seekpos, size(), pos);
	}
}

void ByteStream::seek_cur(size_t& seekpos, size_t curpos, int pos) {
	if (pos > 0) {
		seekpos = std::min<size_t>(curpos + pos, size());
	}
	else {
		seekpos = std::max<size_t>(0, curpos + pos);
	}
}

END_NAMESPACE_TOXIC
